HTMLWidgets.widget({

  name: 'volcanoD3',

  type: 'output',

  factory: function (el, width, height) {

    // TODO: define shared variables for this instance
    d3.select(el).append("svg");
    // d3.select(el).select("svg")
    // .attr("width", width)
    // .attr("height", height);
    return {
      resize: function (el, width, height) {

        // TODO: code to re-render the widget with a new size
      },

      renderValue: function (x) {

        let width = x.myWidth;
        let height = x.myHeight;
        d3.select("svg")
          .attr("width", width)
          .attr("height", height);
        const data = HTMLWidgets.dataframeToD3(x.data); //数据
        function onDownload(data, type, name) {
          const blob = new Blob([data], { type }); // 返回一个新创建的 Blob 对象，其内容由参数中给定的数组串联组成。
          const url = window.URL.createObjectURL(blob); // 创建一个url
          const link = document.createElement('a'); // 创建一个a标签
          link.href = url; // 把url 赋值给a标签的href
          link.style.display = 'none';
          link.setAttribute('download', name);
          document.body.appendChild(link);
          link.click(); // 触发a标签的点击事件
          URL.revokeObjectURL(url); // 清除Url
          document.body.removeChild(link);
        };
        function downloadSVG() { // 保存svg
          const svg = document.querySelector('svg');
          const source = new XMLSerializer().serializeToString(svg); // 将整个SVG document 对象序列化为一个 XML 字符串。
          onDownload(source, 'text/xml', 'volcano.svg'); // 下载 
        }
        function aaa() { // 保存svg
          console.log("下载svg");
          alert("aaa");
          return "svggggggggg"

        }
        
        // 这种格式
        // const data = [
        //     { Name: "id1", FC: "2", PValue: "0.0078", Maker: "" },
        //     { Name: "id2", FC: "0.06", PValue: "0.00129", Maker: "" },
        //     { Name: "id3", FC: "0.3", PValue: "1.3", Maker: "" },
        // ]
        // let width = 600; // 绘图宽
        // let height = 500; // 绘图高
        // let r = 3; //半径
        // let up = "#ff5050";  //  颜色
        // let down = "#26d25d";  //  颜色
        // let notsig = "rgb(101 179 254)";  //  颜色


        // // 坐标轴标签
        // let xAxisLabel1 = 'Log[2]FC'; // X轴标签
        // let yAxisLabel1 = '-Log[10]PValue'; // Y轴标签
        // let xLabelSize = '18px';  // 字体大小
        // let yLabelSize = '18px';
        // let xLabelplot = 'middle'; // 字体位置
        // let yLabelplot = 'middle';
        // let xLabelFamily = 'Arial';  // 字体样式
        // let yLabelFamily = 'Arial';
        // let xLabelWidth = '0'; // 字体粗细 0:不加粗;bold:加粗
        // let yLabelWidth = '0';

        // // 大标题
        // let titlePosition = "0"; // 大标题位置 0:left ; 1:middle; 2:right
        // let titleSize = "16px"; // 大标题字体大小
        // let titleFamily = "Arial"; // 大标题字体样式
        // let titleWidth = "0"; // 大标题字体粗细
        // let titleText = "VolcanoPlot"; // 大标题

        // // 刻度
        // let xFontSize = '14px'; // 字体大小
        // let yFontSize = '14px';
        // let yFontWeight = '0';  // 字体宽度
        // let xFontWeight = '0';
        // let xScaleNumber = '5';  // 刻度个数 5:小于10 ; 10: 大于10
        // let yScaleNumber = '5';
        // let xangle = 0;  // 刻度旋转角度
        // let yangle = 0;
        // let yfonttop = 5; // 刻度距离轴的距离
        // let xfonttop = 5;

        // let fc = '1.5';
        // let pvalue = '0.05';

        // let gridline = "0"; //0: 无网格; 1: 有网格
        // let legendSize = '16px';
        // let legendheight = 10;
        // let legendwidth = 30;
        // let legendPosition = "0"; //0:右方; 1: 下方; 2: 左方; 3: 上方
        // let legendFamily = "Arial";

        // let allFontFamily = "Arial";

        // let shape = "circle";
        // let shapeRound = 4;

        // let infoFamily = "Arial";
        // let infoSize = "16px";
        let r = x.r; //半径
        let up = x.up;  //  颜色
        let down = x.down;  //  颜色
        let notsig = x.notsig;  //  颜色


        // 坐标轴标签
        let xAxisLabel1 = x.xAxisLabel1; // X轴标签
        let yAxisLabel1 = x.yAxisLabel1; // Y轴标签
        let xLabelSize = x.xLabelSize;  // 字体大小
        let yLabelSize = x.yLabelSize;
        let xLabelplot = x.xLabelplot; // 字体位置
        let yLabelplot = x.yLabelplot;
        let xLabelFamily = x.xLabelFamily;  // 字体样式
        let yLabelFamily = x.yLabelFamily;
        let xLabelWidth = x.xLabelWidth; // 字体粗细 0:不加粗;bold:加粗
        let yLabelWidth = x.yLabelWidth;

        // 大标题
        let titlePosition = x.titlePosition; // 大标题位置 0:left ; 1:middle; 2:right
        let titleSize = x.titleSize; // 大标题字体大小
        let titleFamily = x.titleFamily // 大标题字体样式
        let titleWidth = x.titleWidth; // 大标题字体粗细
        let titleText = x.titleText; // 大标题

        // 刻度
        let xFontSize = x.xFontSize; // 字体大小
        let yFontSize = x.yFontSize;
        let yFontWeight = x.yFontWeight;  // 字体宽度
        let xFontWeight = x.xFontWeight;
        let xScaleNumber = x.xScaleNumber;  // 刻度个数 5:小于10 ; 10: 大于10
        let yScaleNumber = x.yScaleNumber;
        let xangle = x.xangle;  // 刻度旋转角度
        let yangle = x.yangle;
        let yfonttop = x.yfonttop; // 刻度距离轴的距离
        let xfonttop = x.xfonttop;

        let fc = x.fc;
        let pvalue = x.pvalue;

        let gridline = x.gridline; //0: 无网格; 1: 有网格
        let legendSize = x.legendSize;
        let legendheight = x.legendheight;
        let legendwidth = x.legendwidth;
        let legendPosition = x.legendPosition; //0:右方; 1: 下方; 2: 左方; 3: 上方
        let legendFamily = x.legendFamily;

        let allFontFamily = x.allFontFamily;

        let shape = x.shape;
        let shapeRound = x.shapeRound;

        let infoFamily = x.infoFamily;
        let infoSize = x.infoSize;
        //传入参数到这里结束

        // 下面的都不用传
        var arr = [];
        var color = {
          green: down,
          red: up,
          blue: notsig,
          black: "orange",
        }
        var round = {
          green: r,
          red: r,
          blue: r,
          black: r + 4,
        }
        //图例数组，格式可自定义
        var data_legend = [
          {
            "name": "Down",
            "color": down,
          },
          {
            "name": "Up",
            "color": up,
          },
          {
            "name": "NotSig",
            "color": notsig,
          },
        ];

        const svg = d3.select('svg');
        const margin = { top: 60, right: 100, bottom: 80, left: 100 };
        let innerWidth = width - margin.left - margin.right;
        let innerHeight = height - margin.top - margin.bottom;
        let xScale, yScale;
        let xAxisLabel = xAxisLabel1;
        let yAxisLabel = yAxisLabel1;
        let xValue = d => Math.log2(d['FC'] + 0);
        let yValue = d => Math.abs(Math.log10(d['PValue'] + 0));
        let xLabelPosition = innerWidth / 2;
        let yLabelPosition = -innerHeight / 2.5;
        let xgridtheme = 0;
        let ygridtheme = 0;
        let titlesite = 0;
        if (gridline == "1") {
          xgridtheme = -innerHeight;
          ygridtheme = -innerWidth;
        }
        if (xLabelplot == 'left') {
          xLabelPosition = '30px'
        } else if (xLabelplot == 'right') {
          xLabelPosition = innerWidth - 30
        }
        if (yLabelplot == 'top') {
          yLabelPosition = '0'
        } else if (yLabelplot == 'bottom') {
          yLabelPosition = -innerHeight + 90
        }

        //标题位置设置
        if (titlePosition == "0") {
          titlesite = innerWidth / 3;
        } else if (titlePosition == "1") {
          titlesite = innerWidth / 1.3;
        } else {
          titlesite = innerWidth / 1;
        }


        //初始化图例，将data_legend与图例绑定
        var legend = svg.selectAll(".legend")
          .data(data_legend)
          .enter().append("g")
          .attr("class", "legend")
          .attr("transform", function (d, i) {
            if (legendPosition == 0) {
              return "translate(-40," + (i * 20 + innerHeight / 1.7) + ")";
            } else if (legendPosition == 3) {
              return "translate(" + (-i * legendSize * shapeRound - innerWidth / 2) + ", 0)";
            } else if (legendPosition == 1) {
              return "translate(" + (-i * legendSize * shapeRound - innerWidth / 2) + "," + (innerHeight + 110) + ")";
            } else if (legendPosition == 2) {
              return "translate(" + (-innerHeight - 120) + "," + (i * 20 + 200) + ")";
            }

          });  //transform属性便是整个图例的坐标

        //绘制文字后方的颜色框或线
        legend.append("circle")
          .attr("cx", width - 40)
          .attr("cy", 12)
          .attr('r', shapeRound)
          .style("fill", function (d) {
            return d.color
          });

        //绘制图例文字
        legend.append("text")
          .attr("x", width - 30)
          .attr("y", 18)
          .attr('font-family', legendFamily)
          .attr("font-size", legendSize)
          .style("text-align", "left")
          .text(function (d) {
            return d.name;
          });

        //初始化散点图
        const renderinit = function (data) {
          // Linear Scale: Data Space -> Screen Space; 
          xScale = d3.scaleLinear()
            .domain([-d3.max(data, xValue), d3.max(data, xValue)]) // "extent" is equivalent to ; 
            .range([0, innerWidth]);

          // Introducing y-Scale; 
          yScale = d3.scaleLinear()
            .domain([0, d3.max(data, yValue)].reverse()) // remember to use reverse() to make y-axis start from the bottom; 
            .range([0, innerHeight]);



          // 添加标题文本到SVG
          svg.append("text")
            .attr("class", "chart-title")
            .attr("x", titlesite)
            .attr('font-size', titleSize)
            .attr('font-family', titleFamily)
            .attr('font-weight', titleWidth)
            .attr("y", 38)
            .attr("text-anchor", "middle")
            .text(titleText);

          // The reason of using group is that nothing is rendered outside svg, so margin of svg is always blank while margin of group is rendered inside svg; 
          //绘制坐标轴
          const g = svg.append('g').attr('id', 'maingroup')
            .attr('transform', `translate(${margin.left}, ${margin.top})`)
            .attr('font-size', '20px')

          //绘制网格线
          const g1 = svg.append('g').attr('id', 'maingroup1')
            .attr('transform', `translate(${margin.left}, ${margin.top})`)
            .attr('stroke-opacity', '0.07')

          const xAxis1 = d3.axisBottom(xScale).ticks(xScaleNumber).tickSize(xgridtheme);
          g1.append('g').call(xAxis1).attr('transform', `translate(${0}, ${innerHeight})`).style('font-size', '0')

          const yAxis1 = d3.axisLeft(yScale).ticks(yScaleNumber).tickSize(ygridtheme);
          g1.append('g').call(yAxis1).style('font-size', '0')


          const xAxis = d3.axisBottom(xScale).ticks(xScaleNumber).tickSize(xgridtheme).tickSizeInner([5]).tickPadding(xfonttop);

          let xgroup = g.append('g').call(xAxis).attr('transform', `translate(${0}, ${innerHeight})`)
            .style('font-size', xFontSize)
            .style('font-weight', xFontWeight)
            .attr('font-family', allFontFamily)

          xgroup.append('text')
            .attr('font-size', xLabelSize)
            .attr('font-family', xLabelFamily)
            .attr('font-weight', xLabelWidth)
            .attr('y', 46)
            .attr('x', xLabelPosition)
            .attr('fill', '#333333')
            .attr('transform', `rotate(0)`)
            .text(xAxisLabel);

          if (xangle !== 0) {
            xgroup.selectAll('.tick text')
              .attr('transform', `rotate(${xangle})`)
              .style('text-anchor', 'end');
          }

          const yAxis = d3.axisLeft(yScale).ticks(yScaleNumber).tickSize(ygridtheme).tickSizeInner([5]).tickPadding(yfonttop);
          let ygroup = g.append('g').call(yAxis)
            .attr('font-size', yFontSize)
            .attr('font-weight', yFontWeight)
            .attr('font-family', allFontFamily)

          ygroup.append('text')
            .attr('font-size', yLabelSize)
            .attr('font-family', yLabelFamily)
            .attr('font-weight', yLabelWidth)
            .attr('transform', `rotate(-90)`)
            .attr('x', yLabelPosition)
            .attr('y', -60)
            .attr('fill', '#333333')
            .text(yAxisLabel)

          // 如果需要旋转 X 轴刻度标签，则将旋转操作应用于刻度文本元素
          if (yangle !== 0) {
            ygroup.selectAll('.tick text')
              .attr('transform', `rotate(${yangle})`)
              .style('text-anchor', 'end');
          }

          const yaveline = yScale(Math.abs(Math.log10(pvalue)))
          //水平辅助线
          const xline = svg.append("line")
            .attr("x1", 0)
            .attr("x2", width - 200)
            .attr("y1", yaveline)
            .attr("y2", yaveline)
            .attr("stroke-width", 1.5)
            .attr("stroke", "black")
            .attr("stroke-dasharray", "5,5")
            .attr('transform', `translate(${margin.left}, ${margin.top})`);
          //垂直辅助线
          const lxline = xScale(Math.log2(1 / fc));
          const rxline = xScale(Math.log2(fc));
          //垂直辅助线
          const ylline = svg.append("line")
            .attr("x1", lxline)
            .attr("x2", lxline)
            .attr("y1", 0)
            .attr("y2", height - 130)
            .attr("stroke-width", 1.5)
            .attr("stroke", "black")
            .attr("stroke-dasharray", "5,5")
            .attr('transform', `translate(${margin.left}, ${margin.top})`);

          const yrline = svg.append("line")
            .attr("x1", rxline)
            .attr("x2", rxline)
            .attr("y1", 0)
            .attr("y2", height - 130)
            .attr("stroke-width", 1.5)
            .attr("stroke", "black")
            .attr("stroke-dasharray", "5,5")
            .attr('transform', `translate(${margin.left}, ${margin.top})`);
        };


        const renderUpdate = function (data) {
          let tooltip = d3.select('body')
            .append('div')
            .style('position', 'absolute')
            .style('display', 'inline-block')
            .style('z-index', '10')
            .style('color', '#fff')
            .style('background', 'black')
            .style('width', '100px')
            .style('height', '60px')
            .style('text-align', 'center')
            .style('padding-top', '5px')
            .style('visibility', 'hidden')   // 是否可见（一开始设置为隐藏）
            .style('font-size', '12px')
            .style('font-weight', 'bold')
            .text('')

          const clickarr = [];
          let infoBoxStartPos = {};
          const g = d3.select("#maingroup");
          let circleupdates = g.selectAll('circle').data(data, d => d['Name']);
          let circleenter = circleupdates.enter().append('circle')
            .attr('cx', d => xScale(xValue(d)))
            .attr('cy', d => yScale(yValue(d)))
            .attr('r', d => round[d['group']])
            .attr('fill', d => color[d['group']])
            .attr('opacity', 0.6)

            .on('click', function (d, i) {
              if (!arr.includes(d)) {
                // if(clickarr.indexOf(d.Name)==-1){
                clickarr.push(d.Name);
                const dd = d;
                const infoBox = svg.append("text")
                  .attr("x", xScale(xValue(d)) + 60)
                  .attr("y", yScale(yValue(d)) + 42)
                  .style("fill", "#000")
                  .style("font-family", infoFamily)
                  .style('font-size', infoSize)
                  .text(d.Name);
                // 绘制连接线
                const lines = svg.append("line")
                  .attr("x1", xScale(xValue(d)) + 80)
                  .attr("y1", yScale(yValue(d)) + 42)
                  .attr("x2", xScale(xValue(d)) + 100)
                  .attr("y2", yScale(yValue(d)) + 60)
                  .attr("id", d.Name)
                  .attr("stroke-width", 1)
                  .attr("stroke", "black");
                // 创建一个拖拽行为
                const drag = d3.drag()
                  .on("start", dragstarted)
                  .on("drag", dragged);

                // 应用拖拽行为到infoBox元素上
                infoBox.call(drag);

                // 拖拽开始时的处理函数
                function dragstarted(event, d) {
                  // 记录当前鼠标位置和infoBox的初始位置
                  d3.select(this).raise();
                  infoBoxStartPos = { x: d3.event.x, y: d3.event.y };
                }
                // 拖拽进行中的处理函数
                function dragged(event, d) {

                  // 更新infoBox的位置
                  infoBox.attr("x", d3.event.x)
                    .attr("y", d3.event.y);
                  // 更新连接线的位置
                  lines.attr("x1", +infoBox.attr("x") + 20)
                    .attr("y1", +infoBox.attr("y") + 6)
                    .attr("x2", xScale(xValue(dd)) + 100)
                    .attr("y2", yScale(yValue(dd)) + 60);
                }
                arr.push(d);
                d3.select(this)
                  // .attr('fill','black')
                  .attr('r', r + 4)
                  .attr('opacity', 1);
                // emit('selectarr',JSON.stringify(arr));

              } else {
                // 如果已被选中，则移除连接线和文字
                d3.selectAll("text").filter(function () {
                  return d3.select(this).text() === d.Name;
                }).remove();
                d3.selectAll("line").filter(function () {
                  return d3.select(this).attr("id") === d.Name;
                }).remove();

                // 从 clickarr 中移除当前元素的名称
                clickarr.splice(clickarr.indexOf(d.Name), 1);
                arr.splice(arr.indexOf(d), 1);
                d3.select(this)
                  // .attr('fill',d=> color[d['group']])
                  .attr('r', r)
                  .attr('opacity', 0.6);
                // emit('selectarr',JSON.stringify(arr));
              }
            })
            .on('mouseover', function (d, i) {
              d3.select(this)
                .attr('opacity', 1)
                .attr('stroke', d => color[d['group']])
                .attr('stroke-width', '5px')
                .attr('stroke-opacity', '0.6')
                .attr('cursor', 'pointer')

              return tooltip.style('visibility', 'visible')
                .text("Name:" + d.Name + "  " + "FC:" + d.FC.toFixed(2) + "  " + "PValue:" + d.PValue.toExponential(2))
            })
            .on('mousemove', function (d, i) {
              d3.select(this)
                .attr('stroke', d => color[d['group']])
                .attr('stroke-width', '5px')
                .attr('stroke-opacity', '0.6')
                .attr('opacity', 1)
              return tooltip.style('top', (event.pageY - 10) + 'px').style('left', (event.pageX + 10) + 'px')
            })
            .on('mouseout', function (d, i) {
              d3.select(this)
                .attr('opacity', 0.6)
                .attr('stroke-width', '0')
              return tooltip.style('visibility', 'hidden')
            });


          //套索

        }

        //把字符串转换为数值
        data.forEach(d => {
          //FC: '0.138995', PValue: '0.007489', Marker
          d['FC'] = + (d['FC']);
          d['PValue'] = + (d['PValue']);
          d['Marker'] = + (d['Marker']);
        });
        for (let i = 0; i < data.length; i++) {
          const ele = data[i];

          if (ele.PValue > pvalue) {
            ele.group = "blue";
          } else {
            if (ele.FC < 1 / fc) {
              ele.group = "green"
            } else if (ele.FC > fc) {
              ele.group = "red"
            } else {
              ele.group = "blue"
            }
          }
        }
        for (let i = 0; i < data.length; i++) {
          const e = data[i];
          if (e.PValue == 0) {
            data.splice(i, 1)
          }

        }

        renderinit(data);
        renderUpdate(data);
        // downloadSVG();



      },



    };
  }
});
